﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace BackToBack
{
    #region ForkedAsyncResult Data/State classes
    internal class ForkedAsyncResultData
    {
        private object _syncRoot = new object();
        private int _count = 0;
        private Exception _firstError = null;

        internal Exception FirstError
        {
            get { return _firstError; }
            private set { _firstError = value; }
        }

        internal void AddForked()
        {
            lock (_syncRoot)
            {
                if (_count == -1)
                    throw new InvalidOperationException("AddForked is not allowed once _count reaches '-1' -- it means methods of this class were not used appropriately");

                _count++;
            }
        }

        /// <summary>
        /// Returns true if everything has already completed
        /// </summary>
        internal bool RemoveForked(Exception ex)
        {
            lock (_syncRoot)
            {
                _count--;

                if (ex != null)
                    this.FirstError = ex;

                return _count == -1;
            }
        }

        /// <summary>
        /// Returns true if everything has already completed
        /// </summary>
        internal bool DoneForkingCheckCompletion()
        {
            lock (_syncRoot)
            {
                _count--; // Assumption is that NO more AddForked() possible at this point

                return _count == -1;
            }
        }

        internal bool AnyAsyncForkFailed
        {
            get 
            {
                return this.FirstError != null;
            }
        }
    }

    internal class ForkedAsyncResultState<TInnerAsyncType>
    {
        private AsyncResultNoResult _userAsyncResult;
        private TInnerAsyncType _innerInstance;

        internal ForkedAsyncResultState(AsyncResultNoResult userAsyncResult, TInnerAsyncType innerInstance)
        {
            this.UserAsyncResult = userAsyncResult;
            this.InnerInstance = innerInstance;
        }

        internal AsyncResultNoResult UserAsyncResult
        {
            get { return _userAsyncResult; }
            private set { _userAsyncResult = value; }
        }

        internal TInnerAsyncType InnerInstance
        {
            get { return _innerInstance; }
            private set { _innerInstance = value; }
        }
    }
    #endregion

    #region GenericEndAsyncState class
    internal class GenericEndAsyncState
    {
        private AsyncResultNoResult _userAsyncResult;
        private AsyncCallback _endCallDelegate;

        internal GenericEndAsyncState(AsyncResult<object> userAsyncResult, AsyncCallback endCallDelegate)
        {
            if (endCallDelegate == null)
            {
                throw new InvalidOperationException("endCallDelegate argument MUST NOT be null");
            }

            _userAsyncResult = userAsyncResult;
            _endCallDelegate = endCallDelegate;
        }

        internal AsyncResultNoResult UserAsyncResult
        {
            get { return _userAsyncResult; }
        }

        internal AsyncCallback EndCallDelegate
        {
            get { return _endCallDelegate; }
        }
    }
    #endregion

}
